-
Notifications
You must be signed in to change notification settings - Fork 27
feat: add useMutationObserver #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/useMutationObserver.ts
Outdated
| @@ -0,0 +1,69 @@ | |||
| import useEffect from './useIsomorphicEffect' | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| import useEffect from './useIsomorphicEffect' | |
| import useIsomorphicEffect from './useIsomorphicEffect' |
maybe? otherwise it's less clear what kind of thing we're doing
src/useMutationObserver.ts
Outdated
| const { | ||
| attributeFilter, | ||
| attributeOldValue, | ||
| attributes, | ||
| characterData, | ||
| characterDataOldValue, | ||
| childList, | ||
| subtree, | ||
| } = config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there really no good way to do shallow equality here? seems awkward to destructure config just to pass it back in again "re-structed", as it were
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope :/ could wrap it into a hook but it adds an extra layer of execution that seems worse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i feel like it'd be a useful hook! we'd use it in react-relay-mutation at least...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added!
An effect hook with customizable equality checks
…builtin effect hooks
|
@taion added a few things and made this more robust |
src/useCustomEffect.ts
Outdated
| depsRef.current = dependencies | ||
|
|
||
| if (!prev || !isEqual(dependencies, prev)) { | ||
| return effect() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arg i need to handle the teardown manually i think this will fire too often
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, what you want to do is:
const dependenciesRef = useRef();
dependenciesRef.current = dependencies;
const cleanupRef = useRef(null);
useEffect(() => {
if (cleanupRef.current === null) {
const cleanup = effect();
cleanupRef.current = () => {
if (isEqual(dependenciesRef.current, dependencies)) {
return;
}
cleanupRef.current = null;
cleanup();
};
}
return cleanupRef.current;
});There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. dependenciesRef.current there is already the new value, while dependencies as bound in cleanupRef.current is still the old value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nvm i see what you did, very clever
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, i think it's the only way to do this without fully emulating everything ourselves
though, it's probably a good idea to pass in the "raw" dependencies list to the base effect fn
| function useCustomEffect<TDeps extends DependencyList = DependencyList>( | ||
| effect: EffectCallback, | ||
| dependencies: DependencyList, | ||
| options: CustomEffectOptions<TDeps>, | ||
| ): void |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the point of this overload? it looks like you're parametrizing on TDeps in all cases. so i don't see what could satisfy this overload but not e.g. the main fn defn below?
src/useCustomEffect.ts
Outdated
| depsRef.current = dependencies | ||
|
|
||
| if (!prev || !isEqual(dependencies, prev)) { | ||
| return effect() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, what you want to do is:
const dependenciesRef = useRef();
dependenciesRef.current = dependencies;
const cleanupRef = useRef(null);
useEffect(() => {
if (cleanupRef.current === null) {
const cleanup = effect();
cleanupRef.current = () => {
if (isEqual(dependenciesRef.current, dependencies)) {
return;
}
cleanupRef.current = null;
cleanup();
};
}
return cleanupRef.current;
});
src/useMutationObserver.ts
Outdated
| type Deps = [Element | null | undefined, MutationObserverInit] | ||
|
|
||
| const isDepsEqual: IsEqual<Deps> = (prev, next) => | ||
| prev[0] === next[0] && isEqual(prev[1], next[1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you probably want "shallow equal" for the config.
actually we probably want a helper that does "shallow equal for each member of deps array"
src/useCustomEffect.ts
Outdated
| depsRef.current = dependencies | ||
|
|
||
| if (!prev || !isEqual(dependencies, prev)) { | ||
| return effect() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. dependenciesRef.current there is already the new value, while dependencies as bound in cleanupRef.current is still the old value
| * @param dependencies A list of dependencies | ||
| * @param isEqual A function comparing the next and previous dependencyLists | ||
| */ | ||
| function useCustomEffect<TDeps extends DependencyList = DependencyList>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this typing seems odd to me. DependencyList is typed as ReadonlyArray<any>... but realistically we're going to have something like a tuple type. do tuple types extend read-only arrays of type any?
... apparently so, but it's weird
| function useCustomEffect<TDeps extends DependencyList = DependencyList>( | ||
| effect: EffectCallback, | ||
| dependencies: TDeps, | ||
| isEqualOrOptions: IsEqual<TDeps> | CustomEffectOptions<TDeps>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure why the overloads are needed at all... seems like anything that would pass validation for the overloads would pass validation here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overloads here are mostly for documentation not safety, yes this would be fine by itself but i think the intellisense is usually nicer to read with overloads
taion
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, fair enough. looks good other than the noted issues
src/useMutationObserver.ts
Outdated
| useMountEffect(() => { | ||
| if (!element) return | ||
|
|
||
| observerRef.current = new MutationObserver(fn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| observerRef.current = new MutationObserver(fn) | |
| observerRef.current = new MutationObserver(fn) | |
| observer.observe(element, config) |
don't you need this here? not that there can really be an element on immediate mount
Co-Authored-By: Jimmy Jia <tesrin@gmail.com>
Co-Authored-By: Jimmy Jia <tesrin@gmail.com>

No description provided.